package cn.bisoft.java.util.config;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;

import com.hengpeng.common.util.ClassHelper;
import com.hengpeng.common.xml.Dom4jHelper;

public final class XmlConfigProvider implements ConfigProvider
{
	private static final Log logger = LogFactory.getLog(XmlConfigProvider.class);

	private final Class classname;

	private Map<String, Config> configMap = new HashMap<String, Config>();

	private final String filepath;

	protected XmlConfigProvider(String filepath, Class classname)
	{
		super();
		if (Config.class.isAssignableFrom(classname))
		{
			this.classname = classname;
			this.filepath = filepath;
			this.loadAllConfigs();
		}
		else
		{
			throw new UnsupportedOperationException("��֧�ֵ���������, �������ͱ���̳���Config�ࡣ");
		}
	}

	private void deserialConfig(Element element, Object object)
	{
		if (object == null)
		{
			return;
		}

		Class instanceClass = object.getClass();

		if (Collection.class.isAssignableFrom(instanceClass))
		{
			logger.warn("�����ļ���֧�ּ����������,��ʹ��������ʽ.");
			throw new UnsupportedOperationException("�����ļ���֧�ּ����������,��ʹ��������ʽ.");
		}
		else if (instanceClass.isArray())
		{
			String classname = instanceClass.getComponentType().getName();
			String shortName = ClassHelper.getShortTypeName(classname);
			List list = element.elements(shortName);
			for (int i = 0; i < list.size(); i++)
			{
				Element subelement = (Element) list.get(i);
				try
				{
					Object config = (Object) instanceClass.getComponentType().newInstance();
					this.deserialConfig(subelement, config);
					Array.set(object, i, config);
				}
				catch (Exception e)
				{
					logger.warn("�����л������ļ��е������쳣.", e);
					continue;
				}
			}
		}
		else
		{
			Method[] methods = instanceClass.getMethods();
			for (int i = 0; i < methods.length; i++)
			{
				String methodName = null;
				try
				{
					Method method = methods[i];
					methodName = method.getName();
					if (methodName.startsWith("set") && (method.getParameterTypes().length == 1))
					{
						String propertyName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
						Class paramType = method.getParameterTypes()[0];

						if (paramType.isArray())
						{
							Class elementType = paramType.getComponentType();
							if (elementType.equals(String.class))
							{
								List list = element.elements(propertyName);

								if (list.size() == 0)
								{
									continue;
								}

								Object objArray = Array.newInstance(elementType, list.size());
								for (int j = 0; j < list.size(); j++)
								{
									Element strElement = (Element) list.get(j);
									Array.set(objArray, j, strElement.getText());
								}
								method.invoke(object, new Object[] { objArray });
							}
							else if (elementType.isPrimitive())
							{
								List list = element.elements(propertyName);

								if (list.size() == 0)
								{
									continue;
								}

								Object objArray = Array.newInstance(elementType, list.size());
								if (Boolean.TYPE.equals(elementType))
								{
									for (int j = 0; j < list.size(); j++)
									{
										Element strElement = (Element) list.get(j);
										Array.set(objArray, j, Boolean.valueOf(strElement.getText()));
									}
									method.invoke(object, new Object[] { objArray });
								}
								else if (java.lang.Byte.TYPE.equals(elementType))
								{
									for (int j = 0; j < list.size(); j++)
									{
										Element strElement = (Element) list.get(j);
										Array.set(objArray, j, new Byte(Byte.parseByte(strElement.getText())));
									}
									method.invoke(object, new Object[] { objArray });
								}
								else if (Short.TYPE.equals(elementType))
								{
									for (int j = 0; j < list.size(); j++)
									{
										Element strElement = (Element) list.get(j);
										Array.set(objArray, j, new Short(Short.parseShort(strElement.getText())));
									}
									method.invoke(object, new Object[] { objArray });
								}
								else if (java.lang.Integer.TYPE.equals(elementType))
								{
									for (int j = 0; j < list.size(); j++)
									{
										Element strElement = (Element) list.get(j);
										Array.set(objArray, j, new Integer(Integer.parseInt(strElement.getText())));
									}
									method.invoke(object, new Object[] { objArray });
								}
								else if (java.lang.Long.TYPE.equals(elementType))
								{
									for (int j = 0; j < list.size(); j++)
									{
										Element strElement = (Element) list.get(j);
										Array.set(objArray, j, new Long(Long.parseLong(strElement.getText())));
									}
									method.invoke(object, new Object[] { objArray });
								}
								else if (Float.TYPE.equals(elementType))
								{
									for (int j = 0; j < list.size(); j++)
									{
										Element strElement = (Element) list.get(j);
										Array.set(objArray, j, new Float(Float.parseFloat(strElement.getText())));
									}
									method.invoke(object, new Object[] { objArray });
								}
								else if (java.lang.Double.TYPE.equals(elementType))
								{
									for (int j = 0; j < list.size(); j++)
									{
										Element strElement = (Element) list.get(j);
										Array.set(objArray, j, new Double(Double.parseDouble(strElement.getText())));
									}
									method.invoke(object, new Object[] { objArray });
								}
								else
								{
									logger.warn("�����ļ���֧�ֵ��������.");
									throw new UnsupportedOperationException("�����ļ���֧�ֵ��������.");
								}
							}
							else if (Date.class.isAssignableFrom(elementType))
							{
								List list = element.elements(propertyName);

								if (list.size() == 0)
								{
									continue;
								}

								Object objArray = Array.newInstance(elementType, list.size());
								DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
								for (int j = 0; j < list.size(); j++)
								{
									Element strElement = (Element) list.get(j);
									Array.set(objArray, j, df.parse(strElement.getText()));
								}
								method.invoke(object, new Object[] { objArray });
							}
							else
							{
								String classname = elementType.getName();
								String shortName = ClassHelper.getShortTypeName(classname);
								List list = element.elements(shortName);
								if (list.size() == 0)
								{
									continue;
								}
								Object objArray = Array.newInstance(elementType, list.size());
								this.deserialConfig(element, objArray);
								method.invoke(object, new Object[] { objArray });
							}
						}
						else if (Collection.class.isAssignableFrom(paramType))
						{
							logger.warn("�����ļ���֧�ּ����������,��ʹ��������ʽ.");
							throw new UnsupportedOperationException("�����ļ���֧�ּ����������,��ʹ��������ʽ.");
						}
						else if (paramType.equals(String.class))
						{
							if (element.attribute(propertyName) != null)
							{
								method.invoke(object, new Object[] { element.attribute(propertyName).getText() });
							}
						}
						else if (paramType.isPrimitive())
						{
							if (element.attribute(propertyName) != null)
							{
								if (Boolean.TYPE.equals(paramType))
								{
									method.invoke(object, new Object[] { Boolean.valueOf(element
											.attribute(propertyName).getText()) });
								}
								else if (java.lang.Byte.TYPE.equals(paramType))
								{
									method.invoke(object, new Object[] { new Byte(Byte.parseByte(element.attribute(
											propertyName).getText())) });
								}
								else if (Short.TYPE.equals(paramType))
								{
									method.invoke(object, new Object[] { new Short(Short.parseShort(element.attribute(
											propertyName).getText())) });
								}
								else if (java.lang.Integer.TYPE.equals(paramType))
								{
									method.invoke(object, new Object[] { new Integer(Integer.parseInt(element
											.attribute(propertyName).getText())) });
								}
								else if (java.lang.Long.TYPE.equals(paramType))
								{
									method.invoke(object, new Object[] { new Long(Long.parseLong(element.attribute(
											propertyName).getText())) });
								}
								else if (Float.TYPE.equals(paramType))
								{
									method.invoke(object, new Object[] { new Float(Float.parseFloat(element.attribute(
											propertyName).getText())) });
								}
								else if (java.lang.Double.TYPE.equals(paramType))
								{
									method.invoke(object, new Object[] { new Double(Double.parseDouble(element
											.attribute(propertyName).getText())) });
								}
								else
								{
									logger.warn("�����ļ���֧�ֵ��������.");
									throw new UnsupportedOperationException("�����ļ���֧�ֵ��������.");
								}
							}
						}
						else if (Date.class.isAssignableFrom(paramType))
						{
							if (element.attribute(propertyName) != null)
							{
								DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
								method.invoke(object, new Object[] { df
										.parse(element.attribute(propertyName).getText()) });
							}
						}
						else
						{
							String classname = paramType.getName();
							String shortName = ClassHelper.getShortTypeName(classname);
							Element subelement = element.element(shortName);
							try
							{
								Object config = (Object) paramType.newInstance();
								this.deserialConfig(subelement, config);
								method.invoke(object, new Object[] { config });
							}
							catch (Exception e)
							{
								logger.warn("�����л������ļ��еĶ����쳣.", e);
								continue;
							}
						}
					}
				}
				catch (Exception e)
				{
					logger.warn("�����л������ļ��е������쳣, �������[" + methodName + "]", e);
					continue;
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.hengpeng.util.configuration.ConfigProvider#loadAllConfig()
	 */
	public Map<String, Config> getAllConfig()
	{
		return this.configMap;
	}

	/*
	 * (non-Javadoc)
	 * @see com.hengpeng.util.configuration.ConfigProvider#loadConfigByPrimaryKey(java.lang.String)
	 */
	public Object getConfigByPrimaryKey(String keyValue)
	{
		return this.configMap.get(keyValue);
	}

	private void loadAllConfigs()
	{
		this.configMap.clear();
		Document doc = Dom4jHelper.getDocument(this.filepath);
		List list = doc.getRootElement().elements();
		String keyName = doc.getRootElement().attribute("key").getText();
		keyName = keyName.substring(0, 1).toUpperCase() + keyName.substring(1);
		String getName = "get" + keyName;
		Object object = Array.newInstance(this.classname, list.size());
		this.deserialConfig(doc.getRootElement(), object);
		for (int i = 0; i < list.size(); i++)
		{
			Config config = (Config) Array.get(object, i);
			if (config == null)
			{
				continue;
			}
			try
			{
				Method getmethod = config.getClass().getMethod(getName);
				Object keyvalue = getmethod.invoke(config, new Object[] {});
				if (keyvalue.getClass().isPrimitive() || keyvalue.getClass().equals(String.class))
				{
					String keystr = String.valueOf(keyvalue);
					config.setKey(keystr);
					if (!this.configMap.containsKey(keystr))
					{
						this.configMap.put(keystr, config);
					}
				}
			}
			catch (Exception e)
			{
				logger.error("��ȡ�����ļ������ʱ���쳣.", e);
			}
		}
	}

	private void loadConfig(String keyValue)
	{
		if (this.configMap.containsKey(keyValue))
		{
			this.configMap.remove(keyValue);
		}

		try
		{
			Config config = (Config) this.classname.newInstance();
			Document doc = Dom4jHelper.getDocument(this.filepath);
			List list = doc.getRootElement().elements();
			for (Iterator iter = list.iterator(); iter.hasNext();)
			{
				Element element = (Element) iter.next();
				if (element.attribute(doc.getRootElement().attribute("key").getText()).getText().equalsIgnoreCase(
						keyValue))
				{
					this.deserialConfig(element, config);
					config.setKey(keyValue);
					this.configMap.put(keyValue, config);
				}
			}
		}
		catch (InstantiationException e)
		{
			logger.error("����ͨ��XML�ļ�װ��������Ϣ�쳣", e);
		}
		catch (IllegalAccessException e)
		{
			logger.error("����ͨ��XML�ļ�װ��������Ϣ�쳣", e);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.hengpeng.util.configuration.ConfigProvider#reloadAllConfig()
	 */
	public void reloadAllConfig()
	{
		this.loadAllConfigs();
	}

	/*
	 * (non-Javadoc)
	 * @see com.hengpeng.util.configuration.ConfigProvider#reloadConfigByPrimaryKey(java.lang.String)
	 */
	public void reloadConfigByPrimaryKey(String keyValue)
	{
		this.loadConfig(keyValue);
	}
}
